/*
 * Copyright (c) 2009 Roman Savelyev
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <f32image.h>
#include <e32std.h>
#include "ImportsWalker.h"

static const TUint8 KOpenCurly = '{';
static const TUint8 KCloseCurly = '}';
static const TUint8 KZeroTerminator = 0;

TImportsWalker::TImportsWalker()
    {
    Reset();
    }

void TImportsWalker::Reset()
    {
    iNumberOfImports = KErrNotReady;
    iImportSection = NULL;
    iImportBlocks.Set(KNullDesC8);
    iLibraryName = KNullDesC8;
    iBranchOffset = KErrNotFound;
    iCurrentImport = 0;
    }

TInt TImportsWalker::Set(const E32ImportSection& aImportSection, TInt aNumberOfBlocks)
    {
    TInt err = KErrNone;
    Reset();
    iImportSection = (const TUint8*)&aImportSection;
    TInt blocksAreaSize = 0;
    iNumberOfImports = 0;
    // first pass - find import blocks area size and number of imports
    const E32ImportBlock* block = (const E32ImportBlock*)(&aImportSection + 1);
    for (TInt i=0; i<aNumberOfBlocks && KErrNone == err; i++)
    	{
    	if (0 >= block->iNumberOfImports)
    		{
    		err = KErrCorrupt;
    		}
    	else
    		{
    		iNumberOfImports += block->iNumberOfImports;
    		blocksAreaSize += block->Size(KImageImpFmt_ELF);
    		block = block->NextBlock(KImageImpFmt_ELF);
    		}
    	}
    iImportBlocks.Set(iImportSection + sizeof(E32ImportSection), blocksAreaSize);
    // second pass - validate DLL names
    block = (const E32ImportBlock*)(&aImportSection + 1);
    for (TInt i=0; i<aNumberOfBlocks && KErrNone == err; i++)
        {
        if (iImportBlocks.Length() + sizeof(E32ImportSection) > block->iOffsetOfDllName)
            {
            err = KErrCorrupt;
            }
        else
            {
            TInt dllNameLength = LibraryNameLength(*block);
            if (0 == dllNameLength)
            	{
            	err = KErrCorrupt;
            	}
            else
            	{
            	TPtrC8 dllName(iImportSection + block->iOffsetOfDllName, dllNameLength);
                TUint32 dummySID;
                TPtrC8 dummyName, dummyTail;
                err = ExtractSID(dllName, dummyName, dummyTail, dummySID);
                block = block->NextBlock(KImageImpFmt_ELF);
            	}
            }
        }
    if (KErrNone != err)
        {
        Reset();
        }
    return err;
    }

TInt TImportsWalker::Next()
    {
    TInt err = KErrNotFound;
    if (!iImportSection)
        {
        err = KErrNotReady;
        }
    else if (0 < iImportBlocks.Length())
        {
        err = KErrNone;
        const E32ImportBlock& importBlock = *(const E32ImportBlock*)iImportBlocks.Ptr();
        if (0 == iLibraryName.Length())
            {
            // started analyzing new import block
            TPtrC8 libName(iImportSection + importBlock.iOffsetOfDllName);
            TUint32 sidValue = KNullUidValue;
            TPtrC8 name, tail;
            ExtractSID(libName, name, tail, sidValue);
            iLibraryName.Append(name);
            iLibraryName.Append(tail);
            iUids = TUidType(KNullUid, KNullUid, TUid::Uid(sidValue));
            err = Next();
            }
        else if (importBlock.iNumberOfImports == iCurrentImport)
            {
            // went through all imports in this block
            // recursively jump to the next one
            iLibraryName.Zero();
            iCurrentImport = 0;
            iImportBlocks.Set(iImportBlocks.Mid(importBlock.Size(KImageImpFmt_ELF)));
            err = Next();
            }
        else
            {
            iBranchOffset = importBlock.Imports()[iCurrentImport++];
            }
        }
    return err;
    }

TInt TImportsWalker::LibraryNameLength(const E32ImportBlock& aImportBlock) const
    {
    const E32ImportSection& importSection = *(const E32ImportSection*)iImportSection;
    TInt err = KErrCorrupt;
    TInt pos = aImportBlock.iOffsetOfDllName;
    for (; KErrCorrupt == err && pos < importSection.iSize; pos++)
        {
        if (KZeroTerminator == iImportSection[pos])
            {
            err = KErrNone;
            }
        }
    return KErrNone == err ? pos - aImportBlock.iOffsetOfDllName : 0;
    }

TInt TImportsWalker::ExtractSID(const TDesC8& aDllName, TPtrC8& aName, TPtrC8& aTail, TUint32& aSID)
	{
	TInt err = KErrNone;
    TInt openCurlyPos = aDllName.Locate(KOpenCurly);
    TInt closeCurlyPos = aDllName.LocateReverse(KCloseCurly);
    if (KErrNotFound == openCurlyPos && KErrNotFound != closeCurlyPos)
    	{
    	// only { is found - corrupt
    	err = KErrCorrupt;
    	}
    else if (KErrNotFound != openCurlyPos && KErrNotFound == closeCurlyPos)
    	{
    	// only } is found - corrupt
    	err = KErrCorrupt;
    	}
    else if (KErrNotFound == openCurlyPos && KErrNotFound == closeCurlyPos)
    	{
    	// neither { nor } is found
    	aName.Set(aDllName);
    	aTail.Set(KNullDesC8);
    	aSID = KNullUidValue;
    	}
    else
        {
        // both { and } found
        aName.Set(aDllName.Left(openCurlyPos));
        aTail.Set(aDllName.Mid(closeCurlyPos+1));
		TLex8 lex(aDllName.Mid(openCurlyPos+1, closeCurlyPos - 1 - openCurlyPos));
		if (KErrNone != lex.Val(aSID, EHex) || !lex.Eos())
			{
			err = KErrCorrupt;
			}
        }
    return err;
	}
